package com.crux.ribbon;

import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.CompletionContext;
import org.springframework.cloud.client.loadbalancer.RequestData;
import org.springframework.cloud.client.loadbalancer.RequestDataContext;
import org.springframework.cloud.client.loadbalancer.ResponseData;
import org.springframework.util.StringUtils;

public class MyLoadBalancerTags {
    static final String UNKNOWN = "UNKNOWN";

    private MyLoadBalancerTags() {
        throw new UnsupportedOperationException("Cannot instantiate utility class");
    }

    static Iterable<Tag> buildSuccessRequestTags(CompletionContext<Object, ServiceInstance, Object> completionContext) {
        ServiceInstance serviceInstance = (ServiceInstance)completionContext.getLoadBalancerResponse().getServer();
        Tags tags = Tags.of(buildServiceInstanceTags(serviceInstance));
        Object clientResponse = completionContext.getClientResponse();
        if (clientResponse instanceof ResponseData) {
            ResponseData responseData = (ResponseData)clientResponse;
            RequestData requestData = responseData.getRequestData();
            if (requestData != null) {
                tags = tags.and(new Tag[]{valueOrUnknown("method", (Object)requestData.getHttpMethod()), valueOrUnknown("uri", getPath(requestData))});
            } else {
                tags = tags.and(new Tag[]{Tag.of("method", "UNKNOWN"), Tag.of("uri", "UNKNOWN")});
            }

            tags = tags.and(new Tag[]{Tag.of("outcome", forStatus(statusValue(responseData))), valueOrUnknown("status", (Object)statusValue(responseData))});
        } else {
            tags = tags.and(new Tag[]{Tag.of("method", "UNKNOWN"), Tag.of("uri", "UNKNOWN"), Tag.of("outcome", "UNKNOWN"), Tag.of("status", "UNKNOWN")});
        }

        return tags;
    }

    private static int statusValue(ResponseData responseData) {
        return responseData.getHttpStatus() != null ? responseData.getHttpStatus().value() : 200;
    }

    private static String getPath(RequestData requestData) {
        return requestData.getUrl() != null ? requestData.getUrl().getPath() : "UNKNOWN";
    }

    static Iterable<Tag> buildDiscardedRequestTags(CompletionContext<Object, ServiceInstance, Object> completionContext) {
        if (completionContext.getLoadBalancerRequest().getContext() instanceof RequestDataContext) {
            RequestData requestData = ((RequestDataContext)completionContext.getLoadBalancerRequest().getContext()).getClientRequest();
            if (requestData != null) {
                return Tags.of(new Tag[]{valueOrUnknown("method", (Object)requestData.getHttpMethod()), valueOrUnknown("uri", getPath(requestData)), valueOrUnknown("serviceId", getHost(requestData))});
            }
        }

        return Tags.of(new Tag[]{valueOrUnknown("method", "UNKNOWN"), valueOrUnknown("uri", "UNKNOWN"), valueOrUnknown("serviceId", "UNKNOWN")});
    }

    private static String getHost(RequestData requestData) {
        return requestData.getUrl() != null ? requestData.getUrl().getHost() : "UNKNOWN";
    }

    static Iterable<Tag> buildFailedRequestTags(CompletionContext<Object, ServiceInstance, Object> completionContext) {
        ServiceInstance serviceInstance = (ServiceInstance)completionContext.getLoadBalancerResponse().getServer();
        Tags tags = Tags.of(buildServiceInstanceTags(serviceInstance)).and(new Tag[]{exception(completionContext.getThrowable())});
        if (completionContext.getLoadBalancerRequest().getContext() instanceof RequestDataContext) {
            RequestData requestData = ((RequestDataContext)completionContext.getLoadBalancerRequest().getContext()).getClientRequest();
            if (requestData != null) {
                return tags.and(Tags.of(new Tag[]{valueOrUnknown("method", (Object)requestData.getHttpMethod()), valueOrUnknown("uri", getPath(requestData))}));
            }
        }

        return tags.and(Tags.of(new Tag[]{valueOrUnknown("method", "UNKNOWN"), valueOrUnknown("uri", "UNKNOWN")}));
    }

    static Iterable<Tag> buildServiceInstanceTags(ServiceInstance serviceInstance) {
        return Tags.of(new Tag[]{valueOrUnknown("serviceId", serviceInstance.getServiceId()), valueOrUnknown("serviceInstance.instanceId", serviceInstance.getInstanceId()), valueOrUnknown("serviceInstance.host", serviceInstance.getHost()), valueOrUnknown("serviceInstance.port", String.valueOf(serviceInstance.getPort()))});
    }

    private static Tag valueOrUnknown(String key, String value) {
        return value != null ? Tag.of(key, value) : Tag.of(key, "UNKNOWN");
    }

    private static Tag valueOrUnknown(String key, Object value) {
        return value != null ? Tag.of(key, String.valueOf(value)) : Tag.of(key, "UNKNOWN");
    }

    private static Tag exception(Throwable exception) {
        if (exception != null) {
            String simpleName = exception.getClass().getSimpleName();
            return Tag.of("exception", StringUtils.hasText(simpleName) ? simpleName : exception.getClass().getName());
        } else {
            return Tag.of("exception", "None");
        }
    }

    public static String forStatus(int status) {
        if (status >= 100 && status < 200) {
            return "INFORMATIONAL";
        } else if (status >= 200 && status < 300) {
            return "SUCCESS";
        } else if (status >= 300 && status < 400) {
            return "REDIRECTION";
        } else if (status >= 400 && status < 500) {
            return "CLIENT_ERROR";
        } else {
            return status >= 500 && status < 600 ? "SERVER_ERROR" : "UNKNOWN";
        }
    }
}
